import re
import os
import time
import json
import jsonpath
import requests
import argparse
import blackboxprotobuf
import pandas as pd
from lxml import etree

headers = {
    "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36",
    'cookie': "buvid3=D58AAA60-93B2-DACC-0DB0-0870FAA9BD4176340infoc; _uuid=3B29999B-104510-788D-410F7-664B19F4D36377031infoc; buvid_fp_plain=undefined; i-wanna-go-back=-1; blackside_state=0; LIVE_BUVID=AUTO6416432875406345; b_nut=100; DedeUserID=353269387; DedeUserID__ckMd5=a36e5efaf4685e30; nostalgia_conf=-1; b_ut=5; buvid4=C95D0336-7388-B133-0DFC-B952FF303FF195018-022012614-tgR3GWNQu%2F4D%2B6qF6CoWTw%3D%3D; hit-new-style-dyn=0; hit-dyn-v2=1; CURRENT_BLACKGAP=0; rpdid=|(J~|Rklm)lm0J'uYY)~~JmlY; CURRENT_FNVAL=4048; CURRENT_QUALITY=120; fingerprint=1b0e767d728d1da779f1f26b8ed1aff7; PVID=6; bp_video_offset_353269387=742301774942044300; SESSDATA=9223be4f%2C1687243444%2C5d06c%2Ac2; bili_jct=b291955b4e9117f4a75ad84e82b5d656; sid=5zvn13h3; b_lsid=FFAE7617_185389A755A; innersign=1; buvid_fp=D58AAA60-93B2-DACC-0DB0-0870FAA9BD4176340infoc"
}

CID = None
COMMENTS_MONTH_DIR = None


def save_file(content, file_name, extension, encoding='utf-8'):
    # 保存获取到的xml文件
    with open(file_name + '.' + extension, 'w', encoding=encoding) as fp:
        fp.write(content)


def get_parameter(bv_id):
    v_url = 'https://www.bilibili.com/video/' + bv_id
    response = requests.get(v_url, headers=headers)
    content = response.text

    regex = re.compile(r'"cid":(\d+),')
    cid = regex.search(content).group(1)  # 正则匹配
    return cid


def get_cur_comm(cid, save=True, mode=1, pid=1):
    if mode == 1:
        url = f'https://comment.bilibili.com/{cid}.xml'
        # print(comment_url)
        response = requests.get(url, headers=headers)
        content = response.content.decode("utf-8")  # 对二进制数据用utf8解码

        if save:
            save_file(content, 'comment_'+cid, 'xml')

        tree = etree.HTML(content.encode("utf-8"))
        comment_li = tree.xpath("//d/text()")  # 筛选出所有d标签的内容
        # print(comment_li)
        return comment_li
    else:
        url = f'https://api.bilibili.com/x/v2/dm/web/seg.so?type=1&oid={cid}&pid={pid})&segment_index=1&pull_mode=1&ps=0&pe=120000'
        response = requests.get(url, headers=headers)
        content = response.content  # 注意，该接口返回的是protobuf格式的数据
        content, typedef = blackboxprotobuf.protobuf_to_json(content)  # 将protobuf格式转换为json格式
        date_str = time.strftime("%Y-%m-%d", time.localtime())
        file_name = f'{date_str}-{cid}'
        save_file(content, file_name, 'json')
        content_json = json.load(open(file_name + '.json', 'r', encoding='utf-8'))
        comment_li = jsonpath.jsonpath(content_json, '$.1[*].7')  # 获取弹幕内容
        return comment_li


def find_cur_comm():
    comment_li = get_cur_comm(CID)
    date_str = time.strftime("%Y-%m-%d", time.localtime())
    # 存储为DataFrame数据
    df = pd.DataFrame.from_dict({date_str: comment_li}, orient='index')
    # 导出(pip install openpyxl)
    df.to_excel("cur_comments.xlsx")
    print("弹幕数据保存成功")


def get_valid_date(cid, month):
    url = f'https://api.bilibili.com/x/v2/dm/history/index?month={month}&type=1&oid={cid}'
    response = requests.get(url, headers=headers)
    content = response.text
    content_json = json.loads(content)
    code = jsonpath.jsonpath(content_json, '$.code')[0]
    if code != 0:
        msg = jsonpath.jsonpath(content_json, '$.message')
        print("请求失败")
        return -1
    date_li = jsonpath.jsonpath(content_json, '$.data[*]')
    # print(date_li)
    return date_li


def get_histry_comm(cid, date):
    if type(cid) is not str or type(date) is not str:
        raise TypeError
    # 通过观察浏览器发送的post请求中的方式，发现oid和cid的值是相同的
    # 点击视频页面的"弹幕列表"就会调用此接口
    url = f'https://api.bilibili.com/x/v2/dm/web/history/seg.so?type=1&oid={cid}&date={date}'
    response = requests.get(url, headers=headers)
    content = response.content  # 注意，该接口返回的是protobuf格式的数据
    content, typedef = blackboxprotobuf.protobuf_to_json(content)  # 将protobuf格式转换为json格式
    file_name = f'{COMMENTS_MONTH_DIR}comment_{cid}_{date}'
    save_file(content, file_name, 'json')
    content_json = json.load(open(file_name+'.json', 'r', encoding='utf-8'))
    comment_li = jsonpath.jsonpath(content_json, '$.1[*].7')  # 获取弹幕内容
    return comment_li


def find_histry_comm(month):
    date_li = get_valid_date(CID, month)
    print(f"{month}的可选日期为：")
    for date in date_li:
        print(date, end=', ')
    date_input = input(f"请选择一个日期(例如:{date_li[0]}), 若输入'all'则获取本月全部弹幕数据：\n")
    if date_input not in date_li and date_input != 'all':
        print("输入不正确")
        exit(-1)
    month_comment = {}
    # 获取历史弹幕
    if date_input == 'all':
        for date in date_li:
            comment_li = get_histry_comm(CID, date)
            month_comment[date] = comment_li
        # 存储为DataFrame数据
        # orient='index'将key作为行, 因为每天的弹幕数不一致
        df = pd.DataFrame.from_dict(month_comment, orient='index')
        # 导出(pip install openpyxl)
        try:
            df.to_excel("comments_month_all.xlsx")
        except IOError as e:
            print(e)
        print("弹幕数据保存成功")
    else:
        comment_li = get_histry_comm(CID, date_input)
        month_comment[date_input] = comment_li
        # 存储为DataFrame数据
        df = pd.DataFrame.from_dict(month_comment, orient='index')
        # 导出(pip install openpyxl)
        try:
            df.to_excel("comments_day.xlsx")
        except IOError as e:
            print(e)
        print("弹幕数据保存成功")


if __name__ == '__main__':
    parse = argparse.ArgumentParser()
    parse.add_argument("--bvid", type=str, required=True, default='1', help="the video's bv id")
    parse.add_argument("--flag", type=str, required=False, help="option flag")
    parse.add_argument("--month", type=str, required=False, help="example: 2022-12")

    args = parse.parse_args()  # 解析参数
    flag = args.flag
    bv_id = args.bvid
    month = args.month

    CID = get_parameter(bv_id)

    if flag == '1':
        find_cur_comm()
    elif flag == '2':
        if month is None:
            raise ValueError("缺少month参数")
        COMMENTS_MONTH_DIR = month+'/'
        if not os.path.isdir(COMMENTS_MONTH_DIR):
            os.mkdir(COMMENTS_MONTH_DIR)
        find_histry_comm(month)



